/** DirectX renderer for GSP260
**
** @version: $Id: Renderer.h 100 2010-12-03 23:12:06Z NeurobaticsTim@gmail.com $
*/
#pragma once

#include <d3d9.h>
#pragma comment(lib, "d3d9.lib")
#include <d3dx9.h>
#pragma comment(lib, "d3dx9.lib")
#include <dxerr.h>
#pragma comment(lib, "dxerr.lib")

#include "Object.h"


/***** Renderer uses the Singleton design pattern *****/


#define SQUARED(x)		((x)*(x))
#define NUM_ELEMENTS(x)	(sizeof(x) / sizeof(x[0]))


const unsigned NUM_LIGHTS = 2;

const D3DXVECTOR3 cvOrigin(0.0f, 0.0f, 0.0f);	// coordinate system origin
const D3DXVECTOR3 ZERO_VECTOR = cvOrigin;

/* Enum for fill mode */
enum FILL_MODE
{
	POINT_FILL	=	D3DFILL_POINT,
	WIREFRAME	=	D3DFILL_WIREFRAME,
	SOLID		=	D3DFILL_SOLID
};


/******************************************************************************\
*                               Renderer class                                 *
\******************************************************************************/
class Renderer
{
public:
	/*****************************************************\
	* Return pointer to the single unique renderer        *
	\*****************************************************/
	inline static Renderer* Instance()
	{
		static Renderer theRenderer;
		return &theRenderer;
	}

	/*****************************************************\
	* Destructor                                          *
	\*****************************************************/
	virtual ~Renderer();

	/*****************************************************\
	* Reclaim locally-created objects, etc.               *
	\*****************************************************/
	void Release();

	/*****************************************************\
	* Start Direct3D in a window                          *
	\*****************************************************/
	void InitializeD3D(HWND hWnd);

	/*****************************************************\
	* Init special stuff for this particular program      *
	\*****************************************************/
	void InitializeDemo();

	/*****************************************************\
	* Draw everything for this one video frame            *
	\*****************************************************/
	void RenderOneFrame(ObjectList* pObjectList);

	/*****************************************************\
	* Set/Get camera position                             *
	\*****************************************************/
	D3DXVECTOR3 GetViewPoint() { return m_vViewPoint; }
	void SetViewPoint(const D3DXVECTOR3& cvNewViewPoint);

	/*****************************************************\
	* Set/Get Fill Mode: { POINT_FILL, SOLID, WIREFRAME } *
	\*****************************************************/
	FILL_MODE GetFillMode() { return (FILL_MODE)m_eFillMode; }
	void SetFillMode(FILL_MODE eMode);

	/*****************************************************\
	* Scene Lights                                        *
	\*****************************************************/
	D3DLIGHT9* GetLight(unsigned nLightnumber);
	bool IsLightOn(int iLightIndex);
	D3DXVECTOR3 GetLightPosition(unsigned nLightNumber);
	void SetLightPosition(unsigned nLightNumber, const D3DXVECTOR3& vNewPosition);
	void ToggleLightEnable(int iLightIndex);

	/*****************************************************\
	* What point should we be looking at?                 *
	\*****************************************************/
	void SetTarget(D3DXVECTOR3 vTarget) { m_vTarget = vTarget; SetViewPoint(m_vViewPoint); }

	/*****************************************************\
	* Set default texture                                 *
	\*****************************************************/
	void SetDefaultTexture(const char* cszFilename);

	/*****************************************************\
	* Load a mesh from a .X file                          *
	\*****************************************************/
	D3DXVECTOR3 LoadMeshFile(Object* pObject, const char* cszXFile);

	/*****************************************************\
	* Load a texture from an image file                   *
	\*****************************************************/
	void LoadTextureFile(const char* cszTextureFile, IDirect3DTexture9** ppTexture);

	/*****************************************************\
	* Make font using convenient arguments                *
	\*****************************************************/
	ID3DXFont* MakeFont(const char* szTypeface,
						int iHeight = 10,
						unsigned uWeight = FW_DONTCARE,
						BOOL bItalic = FALSE,
						UINT uPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE);

	/*****************************************************\
	* Draw text on screen                                 *
	\*****************************************************/
	void DrawString(const char* szText, ID3DXFont* pFont, int iX, int iY, D3DCOLOR cColor);


	/*****************************************************\
	* Draw image on screen at specified location          *
	\*****************************************************/
	void DrawSprite(IDirect3DTexture9* pTexture, int iX, int iY);


private:
	/*****************************************************\
	* Member variables                                    *
	\*****************************************************/
	/* D3D "internal" */
	IDirect3DDevice9*		m_pD3DDevice;			// Direct3D device
	HWND					m_hWnd;					// handle to the window being drawn
	RECT					m_rWindow;				// drawable area
	D3DPRESENT_PARAMETERS	m_xPresentParams;		// for lost surface recovery

	/* Transforms */
	D3DXMATRIX				m_mView;				// VIEW transform matrix
	D3DXMATRIX				m_mProj;				// PROJECTION transform matrix
	D3DXVECTOR3				m_vViewPoint;			// current eye position
	D3DXVECTOR3				m_vTarget;				// where do we look?
	
	/* Window specs */
	float					m_fScreenWidth;			// width of the drawable area of the window
	float					m_fScreenHeight;		// height of the drawable area of the window
	D3DFILLMODE				m_eFillMode;			// current fill mode (D3DFILL_SOLID or D3DFILL_WIREFRAME)

	/* Scene lights */
	D3DLIGHT9				m_cLight[NUM_LIGHTS];	// lights for the scene
	
	/* Defaults */
	IDirect3DTexture9*		m_pDefaultTexture;		// texture to use when none specified
	D3DMATERIAL9			m_cDefaultMaterial;		// material to use when none specified

	/* Drawing strings */
	struct TextBlock								// things required for text rendering
	{
		const char*	szText;							// text to be displayed
		ID3DXFont*	pFont;							// font for the text
		int			iX, iY;							// screen location of upper left corner
		D3DCOLOR	cColor;							// color for text
	};
	typedef std::vector<TextBlock> TextList;		// name for list of strings to be drawn
	#define InsertText(t, L)	L.push_back(t)		// how to add a string to the list
	TextList				m_vTexts;				// list of strings to be drawn this frame
	ID3DXSprite*			m_pSprite;				// for faster text display
	
	/* Drawing sprites */
	struct SpriteBlock								// things required for sprite rendering
	{
		IDirect3DTexture9*	pTexture;				// sprite image
		int					iX, iY;					// screen location
	};
	typedef std::vector<SpriteBlock> SpriteList;	// name for list of sprites to be drawn
	#define InsertSprite(s, L)	L.push_back(s)		// how to add a sprite to the list
	SpriteList				m_vSprites;				// list of sprites to be drawn this frame



	/*****************************************************\
	* Private constructors (for Singleton design pattern) *
	\*****************************************************/
	Renderer();
	Renderer(const Renderer& copyFromMe);


	/*****************************************************\
	* CreateLights                                        *
	\*****************************************************/
	void CreateSceneLights();


	/*****************************************************\
	* CreateMaterial                                      *
	\*****************************************************/
	void CreateMaterial();


	/*****************************************************\
	* IsLost: handle window minimizing, covering, etc.    *
	\*****************************************************/
	bool IsLost();


	/*****************************************************\
	* Render texts                                        *
	\*****************************************************/
	void RenderTexts();


	/*****************************************************\
	* Render sprites                                      *
	\*****************************************************/
	void RenderSprites();


	/* Allow Objects to call RenderObject */
	friend void Object::Render();
	/*****************************************************\
	* RenderObject                                        *
	\*****************************************************/
	void RenderObject(Object* pObject);


	/*****************************************************\
	* Get bounding sphere                                 *
	\*****************************************************/
	void GetBounds(ID3DXMesh* pMesh, D3DXVECTOR3* pvCenter, float* pfSize);
};



/*****************************************************\
* HR macro for error return handling                  *
\*****************************************************/
#ifdef _DEBUG
	#ifndef HR
	#define HR(x)					\
	{								\
		HRESULT hr = x;				\
		if (FAILED(hr))				\
		{							\
			DXTRACE_ERR(#x, hr);	\
			DebugBreak();			\
		}							\
	}
	#endif
#else
	#ifndef HR
	#define HR(x) x;
	#endif
#endif 
